using System;
using System.Collections.Generic;
using System.Text;

namespace SJBotGame.SJBotGameComponents
{
    public class SJBotSheet
    {
        private readonly int xSize;
        public int XSize { get { return xSize; } }
        //
        private readonly int ySize;
        public int YSize { get { return ySize; } }
        //
        private readonly int titleCount;
        private readonly int minInRowCount;
        //
        private SJBotTitle[,] sheet;
        public SJBotTitle GetTitle(Point point) { return sheet[point.X, point.Y]; }
        //
		private Random random;

        public SJBotSheet()
        {
			random = new Random();
			//
            xSize = 20;
            ySize = 20;
            titleCount = 6;
            minInRowCount = 3;
            sheet = new SJBotTitle[xSize, ySize];
            GenerateUniqueSheet();
        }
        public SJBotSheet(int xSize, int ySize, int titleCount)
        {
			random = new Random();
			//
            this.xSize = xSize;
            this.ySize = ySize;
            this.titleCount = titleCount;
            minInRowCount = 3;
            sheet = new SJBotTitle[xSize, ySize];
            GenerateUniqueSheet();
        }
        //
        private void GenerateRandomSheet()
        {
            for (int i = 0; i < XSize; i++)
            {
                for (int j = 0; j < YSize; j++)
                {
                    sheet[i, j] = new SJBotTitle(random.Next(titleCount));
                }
            }
        }
        private void GenerateUniqueSheet()
        {
            for (int i = 0; i < XSize; i++)
            {
                for (int j = 0; j < YSize; j++)
                {
                    sheet[i, j] = new SJBotTitle(random.Next(titleCount));
                    while ( IsInHorizontalRow(new Point(i, j)) || IsInVerticalRow(new Point(i, j)) )
                    {
                        sheet[i, j] = new SJBotTitle(random.Next(titleCount));
                    }
                }
            }
        }
        //

        //
        public void ChangeTitle(Point point1, Point point2)
        {
            SJBotTitle title = sheet[point1.X, point1.Y];
            sheet[point1.X, point1.Y] = sheet[point2.X, point2.Y];
            sheet[point2.X, point2.Y] = title;
			//
			if (!HighlightSheetDraw(point1) && !HighlightSheetDraw(point2))
			{
	            SJBotTitle _title = sheet[point1.X, point1.Y];
	            sheet[point1.X, point1.Y] = sheet[point2.X, point2.Y];
	            sheet[point2.X, point2.Y] = _title;
			}
			else
			{
				UpdateSheet();
			}
		}
		
		private bool HighlightSheetDraw()
        {
			bool isPointRange = false;
            for (int i = 0; i < XSize; i++)
            {
                for (int j = 0; j < YSize; j++)
                {
                    if (HighlightSheetDraw(new Point(i, j))) isPointRange = true;
                }
            }
			return isPointRange;
        }
        private bool HighlightSheetDraw(Point point)
        {
            PointRange hPointRange;
            bool isHPointRange = IsInHorizontalRow(point, out hPointRange);
            //
            PointRange vPointRange;
            bool isVPointRange = IsInVerticalRow(point, out vPointRange);
            //
            if (isHPointRange)
            {
                RemoveTitles(hPointRange);
            }
            if (isVPointRange)
            {
                RemoveTitles(vPointRange);
            }
			return (isHPointRange || isVPointRange);
        }
		private void UpdateSheet()
		{
			bool stillfalling = true;
			while (stillfalling)
			{
				FallDown();
				Update();
				stillfalling = HighlightSheetDraw();
			}
		}
		
        public void RemoveTitles(PointRange pointRange)
        {
            Point[] points = pointRange.GetPoints();
            for (int i = 0; i < points.Length; i++)
            {
                sheet[points[i].X, points[i].Y] = null;
            }
        }
        //
		
		//
		public void Update()
		{
			for (int i = 0; i < xSize; i++)
			{
				for (int j = 0; j < ySize; j++)
				{
					if (sheet[i, j] == null)
					{
						sheet[i, j] = new SJBotTitle(random.Next(titleCount));
					}
				}
			}
		}
		public void FallDown()
		{
			for (int i = 0; i < xSize; i++)
			{
				for (int j = 1; j < ySize; j++)
				{
					if (sheet[i, j] == null)
					{
						for (int k = j; k > 0; k--)
						{
							sheet[i, k] = sheet[i, k - 1];
						}
						sheet[i, 0] = null;
					}
				}
			}
		}

        //
        public bool IsInHorizontalRow(Point point)
        {
            PointRange pointRange;
            return IsInHorizontalRow(point, out pointRange);
        }
        public bool IsInHorizontalRow(Point point, out PointRange pointRange)
        {
            pointRange = new PointRange(point);
            //
            if (GetTitle(point) == null) return false;
            //
			int titleID = GetTitle(point).TitleID;
			for (int i = point.X + 1; i < xSize; i++)
			{
                Point _point = new Point(i, point.Y);
                if ( (GetTitle(_point) != null) && (titleID == GetTitle(_point).TitleID) )
				{
					pointRange.Point1 = new Point(i, point.Y);
				}
				else break;
			}
			for (int i = point.X - 1; i >= 0; i--)
			{
                Point _point = new Point(i, point.Y);
                if ( (GetTitle(_point) != null) && (titleID == GetTitle(_point).TitleID) )
				{
					pointRange.Point2 = new Point(i, point.Y);
				}
				else break;
			}
			return ( pointRange.GetPoints().Length >= minInRowCount );
        }

        public bool IsInVerticalRow(Point point)
        {
            PointRange pointRange;
            return IsInVerticalRow(point, out pointRange);
        }
        public bool IsInVerticalRow(Point point, out PointRange pointRange)
        {
            pointRange = new PointRange(point);
            //
            if (GetTitle(point) == null) return false;
            //
			int titleID = GetTitle(point).TitleID;
			for (int i = point.Y + 1; i < ySize; i++)
			{
                Point _point = new Point(point.X, i);
                if ( (GetTitle(_point) != null) && (titleID == GetTitle(_point).TitleID) )
				{
					pointRange.Point1 = new Point(point.X, i);
				}
				else break;
			}
			for (int i = point.Y - 1; i >= 0; i--)
			{
                Point _point = new Point(point.X, i);
                if ( (GetTitle(_point) != null) && (titleID == GetTitle(_point).TitleID) )
				{
					pointRange.Point2 = new Point(point.X, i);
				}
				else break;
			}
			return ( pointRange.GetPoints().Length >= minInRowCount );
        }
    }
}
